﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using starLiGHT.GUI.Widgets;

namespace starLiGHT.GUI.Input
{
    public class WidgetTextInputTracker : TextInputTracker
    {
        const char BACK_ESCAPE_SEQUENCE = '\b';
        const char RETURN_ESCAPE_SEQUENCE = '\0';
        const char NEW_LINE_ESCAPE_SEQUENCE = '\n';
        const char SHIFT_ESCAPE_SEQUENCE = '\f';

        Dictionary<Widget, KeyValuePair<char, char>> mapping = new Dictionary<Widget, KeyValuePair<char, char>>();

        bool isShifted;
        public bool IsShifted
        {
            get
            {
                return isShifted;
            }
            set
            {
                if (isShifted != value)
                {
                    isShifted = value;
                    UpdateCharacters();

                    if (ShiftChanged != null)
                    {
                        ShiftChanged(this, new EventArgs());
                    }
                }
            }
        }
        public event EventHandler ShiftChanged;


        public void RegisterBackspace(Widget widget)
        {
            RegisterChars(widget, BACK_ESCAPE_SEQUENCE, BACK_ESCAPE_SEQUENCE);
        }

        public void RegisterReturn(Widget widget)
        {
            RegisterChars(widget, RETURN_ESCAPE_SEQUENCE, RETURN_ESCAPE_SEQUENCE);
        }

        [Obsolete("This is not supported by a TextBox")]
        public void RegisterEnter(Widget widget)
        {
            RegisterChars(widget, NEW_LINE_ESCAPE_SEQUENCE, NEW_LINE_ESCAPE_SEQUENCE);
        }

        public void RegisterShift(Widget widget)
        {
            RegisterChars(widget, SHIFT_ESCAPE_SEQUENCE, SHIFT_ESCAPE_SEQUENCE);
        }

        public void RegisterChars(Widget widget, char association, char shiftedAssociation)
        {
            if (widget.CanFocus)
            {
                throw new Exception("only widgets which can not be focused are allowed");
            }

            if (!mapping.ContainsKey(widget))
            {
                mapping.Add(widget, new KeyValuePair<char, char>(association, shiftedAssociation));

                if (!IsSpecialChar(association))
                {
                    widget.Text = association.ToString();
                }
                
                widget.Visible = IsTracking;
                widget.Click += widget_Click;
            }
        }

        void widget_Click(object sender, EventArgs e)
        {
            var kvp = mapping[sender as Widget];
            char c = (isShifted) ? kvp.Value : kvp.Key;

            switch (c)
            {
                case BACK_ESCAPE_SEQUENCE:
                    Backspace();
                    break;

                case RETURN_ESCAPE_SEQUENCE:
                    StopTracking();
                    break;

                case SHIFT_ESCAPE_SEQUENCE:
                    IsShifted = !IsShifted;
                    break;

                default:
                    AddLetter(c);
                    break;
            }
        }

        private void UpdateCharacters()
        {
            foreach (Widget w in mapping.Keys)
            {
                char c = (isShifted) ? mapping[w].Value : mapping[w].Key;

                if(!IsSpecialChar(c))
                {
                    w.Text = c.ToString();
                }
            }
        }

        private static bool IsSpecialChar(char c)
        {
            return c == BACK_ESCAPE_SEQUENCE
                || c == RETURN_ESCAPE_SEQUENCE
                || c == SHIFT_ESCAPE_SEQUENCE
                || c == NEW_LINE_ESCAPE_SEQUENCE
                || c == ' ';
        }

        public override void StartTracking(string text, int caretPosition)
        {
            foreach (Widget w in mapping.Keys)
            {
                w.Visible = true;
            }

            base.StartTracking(text, caretPosition);
        }

        public override void StopTracking()
        {
            foreach (Widget w in mapping.Keys)
            {
                w.Visible = false;
            }

            base.StopTracking();
        }
        

        public override void Update(GameTime gameTime)
        {
            // nothing to do
        }
    }
}
